home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / ip / ka9q / MNetsrc.hqx / Mac TCP_IP Source v.33 / ftpserv.c < prev    next >
Text File  |  1989-03-30  |  20KB  |  849 lines

  1. /* FTP Server state machine - see RFC 959 */
  2.  
  3.  
  4. #include <stdio.h>
  5. #include "global.h"
  6. #include "mbuf.h"
  7. #include "netuser.h"
  8. #include "timer.h"
  9. #include "tcp.h"
  10. #ifdef MAC
  11. #include "mac_files.h"
  12. #include "MacBinary.h"
  13. #endif
  14. #include "ftp.h"
  15.  
  16. #ifdef MAC
  17. #define    LINELEN        256    /* Length of command buffer */
  18. #else
  19. #define    LINELEN        128    /* Length of command buffer */
  20. #endif
  21.  
  22. /* Command table */
  23. static char *commands[] = {
  24.     "user",
  25. #define    USER_CMD    0
  26.     "acct",
  27. #define    ACCT_CMD    1
  28.     "pass",
  29. #define    PASS_CMD    2
  30.     "type",
  31. #define    TYPE_CMD    3
  32.     "list",
  33. #define    LIST_CMD    4
  34.     "cwd",
  35. #define    CWD_CMD        5
  36.     "dele",
  37. #define    DELE_CMD    6
  38.     "name",
  39. #define    NAME_CMD    7
  40.     "quit",
  41. #define    QUIT_CMD    8
  42.     "retr",
  43. #define    RETR_CMD    9
  44.     "stor",
  45. #define    STOR_CMD    10
  46.     "port",
  47. #define    PORT_CMD    11
  48.     "nlst",
  49. #define    NLST_CMD    12
  50.     "pwd",
  51. #define    PWD_CMD        13
  52.     "xpwd",            /* For compatibility with 4.2BSD */
  53. #define    XPWD_CMD    14
  54.     "mkd ",
  55. #define    MKD_CMD        15
  56.     "xmkd",            /* For compatibility with 4.2BSD */
  57. #define    XMKD_CMD    16
  58.     "xrmd",            /* For compatibility with 4.2BSD */
  59. #define    XRMD_CMD    17
  60.     "rmd ",
  61. #define    RMD_CMD        18
  62.     "stru",
  63. #define    STRU_CMD    19
  64.     "mode",
  65. #define    MODE_CMD    20
  66. #ifdef MAC
  67.     "macb",
  68. #define MACB_CMD    21
  69. #endif
  70.     NULLCHAR
  71. };
  72.  
  73. /* Response messages */
  74. static char banner[] = "220 %s FTP version %s ready at %s\r\n";
  75. static char badcmd[] = "500 Unknown command\r\n";
  76. static char unsupp[] = "500 Unsupported command or option\r\n";
  77. static char givepass[] = "331 Enter PASS command\r\n";
  78. static char logged[] = "230 Logged in\r\n";
  79. static char typeok[] = "200 Type OK\r\n";
  80. static char only8[] = "501 Only logical bytesize 8 supported\r\n";
  81. static char deleok[] = "250 File deleted\r\n";
  82. static char mkdok[] = "200 MKD ok\r\n";
  83. static char delefail[] = "550 Delete failed\r\n";
  84. static char pwdmsg[] = "257 \"%s\" is current directory\r\n";
  85. static char badtype[] = "501 Unknown type \"%s\"\r\n";
  86. static char badport[] = "501 Bad port syntax\r\n";
  87. static char unimp[] = "502 Command not yet implemented\r\n";
  88. static char bye[] = "221 Goodbye!\r\n";
  89. static char nodir[] = "553 Can't read directory \"%s\"\r\n";
  90. static char cantopen[] = "550 Can't read file \"%s\"\r\n";
  91. static char sending[] = "150 Opening data connection for %s %s\r\n";
  92. static char cantmake[] = "553 Can't create \"%s\"\r\n";
  93. static char portok[] = "200 Port command okay\r\n";
  94. static char rxok[] = "226 File received OK\r\n";
  95. static char txok[] = "226 File sent OK\r\n";
  96. static char noperm[] = "550 Permission denied\r\n";
  97. static char noconn[] = "425 Data connection reset\r\n";
  98. static char notlog[] = "530 Please log in with USER and PASS\r\n";
  99. static char okay[] = "200 Ok\r\n";
  100. #ifdef MAC
  101. static char mbon[] = "200 MacBinary Mode Enabled\r\n";
  102. static char mboff[] = "200 MacBinary Mode Disabled\r\n";
  103. #endif
  104.  
  105. #ifdef MAC
  106. MBFile *MBopen();
  107. #endif
  108.  
  109. static struct tcb *ftp_tcb;
  110.  
  111. /* Start up FTP service */
  112. ftp1(argc,argv)
  113. int argc;
  114. char *argv[];
  115. {
  116.     struct socket lsocket;
  117.     void ftpscr(),ftpscs();
  118.  
  119.     lsocket.address = ip_addr;
  120.     if(argc < 2)
  121.         lsocket.port = FTP_PORT;
  122.     else
  123.         lsocket.port = atoi(argv[1]);
  124.  
  125.     ftp_tcb = open_tcp(&lsocket,NULLSOCK,TCP_SERVER,0,ftpscr,NULLVFP,ftpscs,0,(char *)NULL);
  126. }
  127. /* Shut down FTP server */
  128. ftp0()
  129. {
  130.     if(ftp_tcb != NULLTCB)
  131.         close_tcp(ftp_tcb);
  132. }
  133. /* FTP Server Control channel State change upcall handler */
  134. static
  135. void
  136. ftpscs(tcb,old,new)
  137. struct tcb *tcb;
  138. char old,new;
  139. {
  140.     extern char hostname[],version[];
  141.     struct ftp *ftp,*ftp_create();
  142.     void ftp_delete();
  143.     char *inet_ntoa();
  144.     long t;
  145.     char *cp,*cp1;
  146.  
  147.     switch(new){
  148. /* Setting QUICKSTART piggybacks the server's banner on the SYN/ACK segment;
  149.  * leaving it unset waits for the three-way handshake to complete before
  150.  * sending the banner. Piggybacking unfortunately breaks some old TCPs,
  151.  * so its use is not (yet) recommended.
  152. */
  153. #ifdef    QUICKSTART
  154.     case SYN_RECEIVED:
  155. #else
  156.     case ESTABLISHED:
  157. #endif
  158.         if((ftp = ftp_create(LINELEN)) == NULLFTP){
  159.             /* No space, kill connection */
  160.             close_tcp(tcb);
  161.             return;
  162.         }
  163.         ftp->control = tcb;        /* Downward link */
  164.         tcb->user = (char *)ftp;    /* Upward link */
  165.  
  166.         /* Set default data port */
  167.         ftp->port.address = tcb->conn.remote.address;
  168.         ftp->port.port = FTPD_PORT;
  169.  
  170.         /* Note current directory */
  171.         log(tcb,"open FTP");
  172.         time(&t);
  173.         cp = ctime(&t);
  174.         if((cp1 = index(cp,'\n')) != NULLCHAR)
  175.             *cp1 = '\0';
  176.         tprintf(ftp->control,banner,hostname,version,cp);
  177.         break;        
  178.     case CLOSE_WAIT:
  179.         close_tcp(tcb);
  180.         break;
  181.     case CLOSED:
  182.         log(tcb,"close FTP");
  183.         if((ftp = (struct ftp *)tcb->user) != NULLFTP)
  184.             ftp_delete(ftp);
  185.         /* Check if server is being shut down */
  186.         if(tcb == ftp_tcb)
  187.             ftp_tcb = NULLTCB;
  188.         del_tcp(tcb);
  189.         break;
  190.     }
  191. }
  192.  
  193. /* FTP Server Control channel Receiver upcall handler */
  194. static
  195. void
  196. ftpscr(tcb,cnt)
  197. struct tcb *tcb;
  198. int16 cnt;
  199. {
  200.     register struct ftp *ftp;
  201.     char c;
  202.     struct mbuf *bp;
  203.     void ftpcommand();
  204.  
  205.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  206.         /* Unknown connection, just kill it */
  207.         close_tcp(tcb);
  208.         return;
  209.     }
  210.     switch(ftp->state){
  211.     case COMMAND_STATE:
  212.         /* Assemble an input line in the session buffer. Return if incomplete */
  213.         recv_tcp(tcb,&bp,0);
  214.         while(pullup(&bp,&c,1) == 1){
  215.             switch(c){
  216.             case '\r':    /* Strip cr's */
  217.                 continue;
  218.             case '\n':    /* Complete line; process it */
  219.                 ftp->buf[ftp->cnt] = '\0';
  220.                 ftpcommand(ftp);
  221.                 ftp->cnt = 0;
  222.                 break;
  223.             default:    /* Assemble line */
  224.                 if(ftp->cnt != LINELEN-1)
  225.                     ftp->buf[ftp->cnt++] = c;
  226.                 break;
  227.             }
  228.         }
  229.         /* else no linefeed present yet to terminate command */
  230.         break;
  231.     case SENDING_STATE:
  232.     case RECEIVING_STATE:
  233.         /* Leave commands pending on receive queue until
  234.          * present command is done
  235.          */
  236.         break;
  237.     }
  238. }
  239.  
  240. /* FTP server data channel connection state change upcall handler */
  241. void
  242. ftpsds(tcb,old,new)
  243. struct tcb *tcb;
  244. char old,new;
  245. {
  246.     register struct ftp *ftp;
  247.  
  248.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  249.         /* Unknown connection. Kill it */
  250.         del_tcp(tcb);
  251.     } else if((old == FINWAIT1 || old == CLOSING) && ftp->state == SENDING_STATE){
  252.         /* We've received an ack of our FIN while sending; we're done */
  253.         ftp->state = COMMAND_STATE;
  254.         tprintf(ftp->control,txok);
  255.         /* Kick command parser if something is waiting */
  256.         if(ftp->control->rcvcnt != 0)
  257.             ftpscr(ftp->control,ftp->control->rcvcnt);
  258.     } else if(ftp->state == RECEIVING_STATE && new == CLOSE_WAIT){
  259.         /* FIN received on incoming file */
  260. #ifdef    CPM
  261.         if(ftp->type == ASCII_TYPE)
  262.             putc(CTLZ,ftp->fp);
  263. #endif
  264.         close_tcp(tcb);
  265. #ifdef MAC
  266.         if(ftp->fp != stdout && ftp->fp != NULLFILE){
  267.             fclose(ftp->fp);
  268.             ftp->fp = NULLFILE;
  269.         } else {
  270.             if(ftp->MacBinary && ftp->MBftp){
  271.                 MBclose(ftp->mbfp);
  272.                 free(ftp->mbfp);
  273.                 ftp->mbfp = NULLMBF;
  274.                 ftp->MBftp = MACB_OFF;
  275.             }
  276.         }
  277. #else
  278.         if(ftp->fp != stdout)
  279.             fclose(ftp->fp);
  280.         ftp->fp = NULLFILE;
  281. #endif
  282.         ftp->state = COMMAND_STATE;
  283.         tprintf(ftp->control,rxok);
  284.         /* Kick command parser if something is waiting */
  285.         if(ftp->control->rcvcnt != 0)
  286.             ftpscr(ftp->control,ftp->control->rcvcnt);
  287.     } else if(new == CLOSED){
  288.         if(tcb->reason != NORMAL){
  289.             /* Data connection was reset, complain about it */
  290.             tprintf(ftp->control,noconn);
  291.             /* And clean up */
  292.             if(ftp->fp != NULLFILE && ftp->fp != stdout)
  293.                 fclose(ftp->fp);
  294.             ftp->fp = NULLFILE;
  295. #ifdef MAC
  296.             if(ftp->MacBinary && ftp->MBftp)
  297.                 if(ftp->mbfp != NULLMBF){
  298.                     MBclose(ftp->mbfp);
  299.                     free(ftp->mbfp);
  300.                     ftp->mbfp = NULLMBF;
  301.                     ftp->MBftp = MACB_OFF;
  302.                 }
  303. #endif
  304.             ftp->state = COMMAND_STATE;
  305.             /* Kick command parser if something is waiting */
  306.             if(ftp->control->rcvcnt != 0)
  307.                 ftpscr(ftp->control,ftp->control->rcvcnt);
  308.         }
  309.         /* Clear only if another transfer hasn't already started */
  310.         if(ftp->data == tcb)
  311.             ftp->data = NULLTCB;
  312.         del_tcp(tcb);
  313.     }
  314. }
  315.  
  316. /* Parse and execute ftp commands */
  317. static
  318. void
  319. ftpcommand(ftp)
  320. register struct ftp *ftp;
  321. {
  322.     void ftpdr(),ftpdt(),ftpsds();
  323.     char *cmd,*arg,*cp,**cmdp,*file;
  324.     char *pathname();
  325.     char *mode;
  326.     struct socket dport;
  327.     
  328. #ifndef    CPM
  329.     FILE *dir();
  330. #endif
  331.  
  332.     cmd = ftp->buf;
  333.     if(ftp->cnt == 0){
  334.         /* Can't be a legal FTP command */
  335.         tprintf(ftp->control,badcmd);
  336.         return;
  337.     }    
  338.     cmd = ftp->buf;
  339.  
  340. #ifdef    UNIX
  341.     /* Translate first word to lower case */
  342.     for(cp = cmd;*cp != ' ' && *cp != '\0';cp++)
  343.         *cp = tolower(*cp);
  344. #else
  345.     /* Translate entire buffer to lower case */
  346.     for(cp = cmd;*cp != '\0';cp++)
  347.         *cp = tolower(*cp);
  348. #endif
  349.     /* Find command in table; if not present, return syntax error */
  350.     for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
  351.         if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
  352.             break;
  353.     if(*cmdp == NULLCHAR){
  354.         tprintf(ftp->control,badcmd);
  355.         return;
  356.     }
  357.     /* Allow only USER, PASS and QUIT before logging in */
  358.     if(ftp->cd == NULLCHAR || ftp->path == NULLCHAR){
  359.         switch(cmdp-commands){
  360.         case USER_CMD:
  361.         case PASS_CMD:
  362.         case QUIT_CMD:
  363.             break;
  364.         default:
  365.             tprintf(ftp->control,notlog);
  366.             return;
  367.         }
  368.     }
  369.     arg = &cmd[strlen(*cmdp)];
  370.     while(*arg == ' ')
  371.         arg++;
  372.  
  373.     /* Execute specific command */
  374.     switch(cmdp-commands){
  375.     case USER_CMD:
  376.         if((ftp->username = malloc((unsigned)strlen(arg)+1)) == NULLCHAR){
  377.             close_tcp(ftp->control);
  378.             break;
  379.         }
  380.         strcpy(ftp->username,arg);
  381.         tprintf(ftp->control,givepass);
  382.         break;
  383.     case TYPE_CMD:
  384.         switch(arg[0]){
  385.         case 'A':
  386.         case 'a':    /* Ascii */
  387.             ftp->type = ASCII_TYPE;
  388.             tprintf(ftp->control,typeok);
  389.             break;
  390.         case 'l':
  391.         case 'L':
  392.             while(*arg != ' ' && *arg != '\0')
  393.                 arg++;
  394.             if(*arg == '\0' || *++arg != '8'){
  395.                 tprintf(ftp->control,only8);
  396.                 break;
  397.             }    /* Note fall-thru */
  398.         case 'B':
  399.         case 'b':    /* Binary */
  400.         case 'I':
  401.         case 'i':    /* Image */
  402.             ftp->type = IMAGE_TYPE;
  403.             tprintf(ftp->control,typeok);
  404.             break;
  405.         default:    /* Invalid */
  406.             tprintf(ftp->control,badtype,arg);
  407.             break;
  408.         }
  409.         break;
  410.     case QUIT_CMD:
  411.         tprintf(ftp->control,bye);
  412.         close_tcp(ftp->control);
  413.         break;
  414.     case RETR_CMD:
  415.         /* Disk operation; return ACK now */
  416.         tcp_output(ftp->control);
  417.         file = pathname(ftp->cd,arg);
  418.         if(ftp->type == IMAGE_TYPE)
  419.             mode = binmode[READ_BINARY];
  420.         else
  421.             mode = "r";
  422.         if(!permcheck(ftp,RETR_CMD,file)){
  423.              tprintf(ftp->control,noperm);
  424. #ifdef MAC
  425.             free(file);
  426.             break;
  427.         }
  428.         if(ftp->MacBinary){
  429.             if((ftp->mbfp=MBopen(file,0,MB_READ + (((!ftp->MacBinary) ||
  430.                 (ftp->type == ASCII_TYPE)) ? MB_DISABLE : 0))) == NULLMBF) {
  431.                     tprintf(ftp->control,cantopen,file);
  432.                     free(file);
  433.                     break;
  434.                 }
  435.             ftp->fp = NULLFILE;
  436.             ftp->MBftp = MACB_ON;
  437.         } else { 
  438.              if((ftp->fp = fopen(file,mode)) == NULLFILE){
  439.                 tprintf(ftp->control,cantopen,file);
  440.                 free(file);
  441.                 break;
  442.             }
  443.         }
  444.         log(ftp->control,"RETR %s",file);
  445.         dport.address = ip_addr;
  446.         dport.port = FTPD_PORT;
  447.         ftp->state = SENDING_STATE;
  448.         tprintf(ftp->control,sending,"RETR",arg);
  449.         ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  450.             0,NULLVFP,ftpdt,ftpsds,ftp->control->tos,(char *)ftp);
  451.         free(file);
  452.         break;
  453. #else
  454.         } else if((ftp->fp = fopen(file,mode)) == NULLFILE){
  455.             tprintf(ftp->control,cantopen,file);
  456.         } else {
  457.             log(ftp->control,"RETR %s",file);
  458.             dport.address = ip_addr;
  459.             dport.port = FTPD_PORT;
  460.             ftp->state = SENDING_STATE;
  461.             tprintf(ftp->control,sending,"RETR",arg);
  462.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  463.              0,NULLVFP,ftpdt,ftpsds,ftp->control->tos,(char *)ftp);
  464.         }
  465.         free(file);
  466.         break;
  467. #endif
  468.     case STOR_CMD:
  469.         /* Disk operation; return ACK now */
  470.         tcp_output(ftp->control);
  471.         file = pathname(ftp->cd,arg);
  472.         if(ftp->type == IMAGE_TYPE)
  473.             mode = binmode[WRITE_BINARY];
  474.         else
  475.             mode = "w";
  476.         if(!permcheck(ftp,STOR_CMD,file)){
  477.              tprintf(ftp->control,noperm);
  478.             free(file);
  479.              break;
  480. #ifdef MAC
  481.         }
  482.         if(ftp->MacBinary){
  483.             if((ftp->mbfp=MBopen(file,0,MB_WRITE + (((!ftp->MacBinary) ||
  484.                 (ftp->type == ASCII_TYPE)) ? MB_DISABLE : 0))) == NULLMBF) {
  485.                     tprintf(ftp->control,cantmake,file);
  486.                     free(file);
  487.                     break;
  488.                 }
  489.             ftp->fp = NULLFILE;
  490.             ftp->MBftp = MACB_ON;
  491.         } else { 
  492.              if((ftp->fp = fopen(file,mode)) == NULLFILE){
  493.                 tprintf(ftp->control,cantmake,file);
  494.                 free(file);
  495.                 break;
  496.             }
  497.         }
  498.         log(ftp->control,"STOR %s",file);
  499.         dport.address = ip_addr;
  500.         dport.port = FTPD_PORT;
  501.         ftp->state = RECEIVING_STATE;
  502.         tprintf(ftp->control,sending,"STOR",arg);
  503.         ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  504.             0,ftpdr,NULLVFP,ftpsds,ftp->control->tos,(char *)ftp);
  505.         free(file);
  506.         break;
  507. #else
  508.         } else if((ftp->fp = fopen(file,mode)) == NULLFILE){
  509.             tprintf(ftp->control,cantmake,file);
  510.         } else {
  511.             log(ftp->control,"STOR %s",file);
  512.             dport.address = ip_addr;
  513.             dport.port = FTPD_PORT;
  514.             ftp->state = RECEIVING_STATE;
  515.             tprintf(ftp->control,sending,"STOR",arg);
  516.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  517.              0,ftpdr,NULLVFP,ftpsds,ftp->control->tos,(char *)ftp);
  518.         }
  519.         free(file);
  520.         break;
  521. #endif
  522.     case PORT_CMD:
  523.         if(pport(&ftp->port,arg) == -1){
  524.             tprintf(ftp->control,badport);
  525.         } else {
  526.             tprintf(ftp->control,portok);
  527.         }
  528.         break;
  529. #ifndef CPM
  530.     case LIST_CMD:
  531.         /* Disk operation; return ACK now */
  532.         tcp_output(ftp->control);
  533.         file = pathname(ftp->cd,arg);
  534.         if(!permcheck(ftp,RETR_CMD,file)){
  535.              tprintf(ftp->control,noperm);
  536.         } else if((ftp->fp = dir(file,1)) == NULLFILE){
  537.             tprintf(ftp->control,nodir,file);
  538.         } else {
  539.             dport.address = ip_addr;
  540.             dport.port = FTPD_PORT;
  541.             ftp->state = SENDING_STATE;
  542.             tprintf(ftp->control,sending,"LIST",file);
  543.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  544.              0,NULLVFP,ftpdt,ftpsds,ftp->control->tos,(char *)ftp);
  545.         }
  546.         free(file);
  547.         break;
  548.     case NLST_CMD:
  549.         /* Disk operation; return ACK now */
  550.         tcp_output(ftp->control);
  551.         file = pathname(ftp->cd,arg);
  552.         if(!permcheck(ftp,RETR_CMD,file)){
  553.              tprintf(ftp->control,noperm);
  554.         } else if((ftp->fp = dir(file,0)) == NULLFILE){
  555.             tprintf(ftp->control,nodir,file);
  556.         } else {
  557.             dport.address = ip_addr;
  558.             dport.port = FTPD_PORT;
  559.             ftp->state = SENDING_STATE;
  560.             tprintf(ftp->control,sending,"NLST",file);
  561.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  562.              0,NULLVFP,ftpdt,ftpsds,ftp->control->tos,(char *)ftp);
  563.         }
  564.         free(file);
  565.         break;
  566.     case CWD_CMD:
  567.         tcp_output(ftp->control);    /* Disk operation; return ACK now */
  568.         file = pathname(ftp->cd,arg);
  569.         if(!permcheck(ftp,RETR_CMD,file)){
  570.              tprintf(ftp->control,noperm);
  571.             free(file);
  572. #ifdef    MSDOS
  573.         /* Don'tcha just LOVE %%$#@!! MS-DOS? */
  574.         } else if(strcmp(file,"\\") == 0 || access(file,0) == 0){
  575. #else
  576.         } else if(access(file,0) == 0){    /* See if it exists */
  577. #endif
  578.             /* Succeeded, record in control block */
  579.             free(ftp->cd);
  580.             ftp->cd = file;
  581.             tprintf(ftp->control,pwdmsg,file);
  582.         } else {
  583.             /* Failed, don't change anything */
  584.             tprintf(ftp->control,nodir,file);
  585.             free(file);
  586.         }
  587.         break;
  588.     case XPWD_CMD:
  589.     case PWD_CMD:
  590.         tprintf(ftp->control,pwdmsg,ftp->cd);
  591.         break;
  592. #else
  593.     case LIST_CMD:
  594.     case NLST_CMD:
  595.     case CWD_CMD:
  596.     case XPWD_CMD:
  597.     case PWD_CMD:
  598. #endif
  599.     case ACCT_CMD:        
  600.         tprintf(ftp->control,unimp);
  601.         break;
  602.     case DELE_CMD:
  603.         file = pathname(ftp->cd,arg);
  604.         if(!permcheck(ftp,DELE_CMD,file)){
  605.              tprintf(ftp->control,noperm);
  606.         } else if(unlink(file) == 0){
  607.             tprintf(ftp->control,deleok);
  608.         } else {
  609.             tprintf(ftp->control,delefail);
  610.         }
  611.         free(file);
  612.         break;
  613.     case PASS_CMD:
  614.         tcp_output(ftp->control);    /* Send the ack now */
  615.         ftplogin(ftp,arg);            
  616.         break;
  617. #ifndef    CPM
  618.     case XMKD_CMD:
  619.     case MKD_CMD:
  620.         file = pathname(ftp->cd,arg);
  621.         if(!permcheck(ftp,MKD_CMD,file)){
  622.             tprintf(ftp->control,noperm);
  623.         } else if(mkdir(file,0777) == 0){
  624.             tprintf(ftp->control,mkdok);
  625.         } else {
  626.             tprintf(ftp->control,cantmake);
  627.         }
  628.         free(file);
  629.         break;
  630.     case XRMD_CMD:
  631.     case RMD_CMD:
  632.         file = pathname(ftp->cd,arg);
  633.         if(!permcheck(ftp,RMD_CMD,file)){
  634.              tprintf(ftp->control,noperm);
  635.         } else if(rmdir(file) == 0){
  636.             tprintf(ftp->control,deleok);
  637.         } else {
  638.             tprintf(ftp->control,delefail);
  639.         }
  640.         free(file);
  641.         break;
  642.     case STRU_CMD:
  643.         if(tolower(arg[0]) != 'f')
  644.             tprintf(ftp->control,unsupp);
  645.         else
  646.             tprintf(ftp->control,okay);
  647.         break;
  648.     case MODE_CMD:
  649.         if(tolower(arg[0]) != 's')
  650.             tprintf(ftp->control,unsupp);
  651.         else
  652.             tprintf(ftp->control,okay);
  653.         break;
  654. #ifdef MAC
  655.     case MACB_CMD:
  656.         if(tolower(arg[0]) == 'e') {
  657.             ftp->MacBinary = MACB_ON;
  658.             tprintf(ftp->control,mbon);
  659.         } else {
  660.             ftp->MacBinary = MACB_OFF;
  661.             tprintf(ftp->control,mboff);
  662.         }
  663.         break;
  664. #endif
  665.     }
  666. #endif
  667. }
  668. static
  669. int
  670. pport(sock,arg)
  671. struct socket *sock;
  672. char *arg;
  673. {
  674.     int32 n;
  675.     int atoi(),i;
  676.  
  677.     n = 0;
  678.     for(i=0;i<4;i++){
  679.         n = atoi(arg) + (n << 8);
  680.         if((arg = index(arg,',')) == NULLCHAR)
  681.             return -1;
  682.         arg++;
  683.     }
  684.     sock->address = n;
  685.     n = atoi(arg);
  686.     if((arg = index(arg,',')) == NULLCHAR)
  687.         return -1;
  688.     arg++;
  689.     n = atoi(arg) + (n << 8);
  690.     sock->port = n;
  691.     return 0;
  692. }
  693. /* Attempt to log in the user whose name is in ftp->username and password
  694.  * in pass
  695.  */
  696. static
  697. ftplogin(ftp,pass)
  698. struct ftp *ftp;
  699. char *pass;
  700. {
  701.     char buf[80],*cp,*cp1;
  702.     FILE *fp;
  703.     int anony = 0;
  704.  
  705.     if((fp = fopen(userfile,"r")) == NULLFILE){
  706.         /* Userfile doesn't exist */
  707.         tprintf(ftp->control,noperm);
  708.         return;
  709.     }
  710.     while(fgets(buf,sizeof(buf),fp),!feof(fp)){
  711.         if(buf[0] == '#')
  712.             continue;    /* Comment */
  713.         if((cp = index(buf,' ')) == NULLCHAR)
  714.             /* Bogus entry */
  715.             continue;
  716.         *cp++ = '\0';        /* Now points to password */
  717.         if(strcmp(ftp->username,buf) == 0)
  718.             break;        /* Found user name */
  719.     }
  720.     if(feof(fp)){
  721.         /* User name not found in file */
  722.         fclose(fp);
  723.         tprintf(ftp->control,noperm);
  724.         return;
  725.     }
  726.     fclose(fp);
  727.     /* Look for space after password field in file */
  728.     if((cp1 = index(cp,' ')) == NULLCHAR){
  729.         /* Invalid file entry */
  730.         tprintf(ftp->control,noperm);
  731.         return;
  732.     }
  733.     *cp1++ = '\0';    /* Now points to path field */
  734.     if(strcmp(cp,"*") == 0)
  735.         anony = 1;    /* User ID is password-free */
  736.     if(!anony && strcmp(cp,pass) != 0){
  737.         /* Password required, but wrong one given */
  738.         tprintf(ftp->control,noperm);
  739.         return;
  740.     }
  741. #ifdef MAC
  742.      /* allow path with embedded spaces if quoted */
  743.      if (*cp1 == '\'') {
  744.           cp1++;
  745.           if ((cp = index(cp1,'\'')) == NULLCHAR) {
  746.             /* closing quote missing */
  747.             tprintf(ftp->control,noperm);
  748.             return;
  749.           }
  750.           *cp++ = '\0';      /* get rid of closing quote */
  751.      } else {
  752.           cp = cp1;
  753.      }
  754.      if((cp = index(cp,' ')) == NULLCHAR){
  755.           /* Permission field missing */
  756.           tprintf(ftp->control,noperm);
  757.           return;
  758.      }
  759. #else
  760.      if((cp = index(cp1,' ')) == NULLCHAR){
  761.           /* Permission field missing */
  762.           tprintf(ftp->control,noperm);
  763.           return;
  764.      }
  765. #endif
  766.      *cp++ = '\0';       /* now points to permission field */
  767.  
  768.     /* Set up current directory and path prefix */
  769. #ifndef MAC    
  770.     ftp->cd = malloc((unsigned)strlen(cp1)+1);
  771.     ftp->path = malloc((unsigned)strlen(cp1)+1);
  772.     strcpy(ftp->cd,cp1);
  773.     strcpy(ftp->path,cp1);
  774. #else
  775.     ftp->cd = pathname(applroot,cp1);
  776.     ftp->path = malloc((unsigned)strlen(ftp->cd)+1);
  777.     strcpy(ftp->path,ftp->cd);
  778. #endif
  779.     
  780.     /* And finally set the permission bits */
  781.     ftp->perms = atoi(cp);
  782.  
  783.     tprintf(ftp->control,logged);
  784.     if(!anony)
  785.         log(ftp->control,"%s logged in",ftp->username);
  786.     else
  787.         log(ftp->control,"%s logged in, ID %s",ftp->username,pass);
  788. }        
  789.  
  790. #ifdef    MSDOS
  791. /* Illegal characters in a DOS filename */
  792. char badchars[] = "\"[]:|<>+=;,";
  793. #endif
  794.  
  795. /* Return 1 if the file operation is allowed, 0 otherwise */
  796. permcheck(ftp,op,file)
  797. struct ftp *ftp;
  798. int op;
  799. char *file;
  800. {
  801.     char *cp;
  802.  
  803.     if(file == NULLCHAR || ftp->path == NULLCHAR)
  804.         return 0;    /* Probably hasn't logged in yet */
  805. #ifdef    MSDOS
  806.     /* Check for characters illegal in MS-DOS file names */
  807.     for(cp = badchars;*cp != '\0';cp++){
  808.         if(index(file,*cp) != NULLCHAR)
  809.             return 0;    
  810.     }
  811. #endif
  812. #if    (defined(AMIGA))
  813. #else
  814.     /* The target file must be under the user's allowed search path */
  815.     if(strncmp(file,ftp->path,strlen(ftp->path)) != 0)
  816.         return 0;
  817. #endif
  818.  
  819.     switch(op){
  820.     case RETR_CMD:
  821.         /* User must have permission to read files */
  822.         if(ftp->perms & FTP_READ)
  823.             return 1;
  824.         return 0;
  825.     case DELE_CMD:
  826.     case RMD_CMD:
  827.         /* User must have permission to (over)write files */
  828.         if(ftp->perms & FTP_WRITE)
  829.             return 1;
  830.         return 0;
  831.     case STOR_CMD:
  832.     case MKD_CMD:
  833.         /* User must have permission to (over)write files, or permission
  834.          * to create them if the file doesn't already exist
  835.          */
  836.         if(ftp->perms & FTP_WRITE)
  837.             return 1;
  838. #ifdef MAC
  839.         if(access(file,2) == 0 && (ftp->perms & FTP_CREATE))
  840. #else
  841.         if(access(file,2) == -1 && (ftp->perms & FTP_CREATE))
  842. #endif
  843.             return 1;
  844.         return 0;
  845.     }
  846.     return 0;    /* "can't happen" -- keep lint happy */
  847. }
  848.  
  849.